package bento.api.virtualDB;

import java.util.*;

import bento.api.AbstractBentoClient;
import bento.api.IBentoClient.IAcountManager.IAcount;
import bento.api.IBentoClient.IManager;
import bento.api.IBentoClient.IStoreManager.IStore;
import bento.api.IBentoClient.IStoreManager.IStore.IBentoManager;
import bento.api.IQuerier.IField;
import bento.api.IQuerier.IWhereThen;
import bento.api.ITransactor;
import bento.api.IQuerier;
import bento.utils.IPredicate;

public class BentoClient extends AbstractBentoClient {
    private IAcountManager.IAcountX acount;
    
    private IGate gate= new IGate() {
        
        @Override
        public boolean logout() {
            // TODO Auto-generated method stub
            return false;
        }
        
        @Override
        public boolean login(final String user, final String password) {
            if (acount!=null) return false;
//            acount= Predicate.select(acounts.cast(), new IPredicate<Acount>() {
//                @Override
//                public boolean apply(Acount acount) {
//                    if (!acount.getName().equals(user)) return false;
//                    if (!acount.getPassword().equals(password)) return false;
//                    return true;
//                }
//            });
            return acount!=null;
        }
    };
    
    private IAcountManager acounts;//= new AbstractAcountManager() {
//        
//        @Override
//        public IAcountX create() {
//            return new Acount();
//        }
//        
//        @Override
//        public void commit() {
//            // TODO Auto-generated method stub
//            
//        }
//        
//        @Override
//        public void refresh() {
//            // TODO Auto-generated method stub
//            
//        }
//    };
    
    
    private IStoreManager stores= new StoreManager();
    
    private IOrderManager orders;//= new AbstractOrderManager() {
//        
//        @Override
//        public void commit() {
//            // TODO Auto-generated method stub
//            
//        }
//        
//        @Override
//        public void refresh() {
//            // TODO Auto-generated method stub
//            
//        }
//    };
    
    @Override
    public IGate getGate() {
        return gate;
    }

    @Override
    public IAcount getAcount() {
        return acount;
    }

    @Override
    public IAcountManager getAcounts() {
        return acounts;
    }

    @Override
    public IStoreManager getStores() {
        return stores;
    }

    @Override
    public IOrderManager getOrders() {
        return orders;
    }
    
    private static abstract class AbstractManager<TElem extends IManager.IElem, TWrapped> 
                   extends AbstractBentoClient.AbstractManager<TElem> {
        
        protected static abstract class Field<TValue, TWrapped> implements IField<TValue> {
            private String name;
            
            public abstract TValue getValue(TWrapped row);
            
            @Override
            public final String getName() {
                return name;
            }
            
            public Field(String name) {
                this.name= name;
            }
        }
        
        protected static abstract class Querier<TElem extends IManager.IElem, TWrapped> implements IQuerier<TElem> {
            private List<TWrapped> virtualDBSource = new ArrayList<>();
            private List<IPredicate<TWrapped>> lst_filter= new ArrayList<>();
            
            protected abstract TElem wrap(TWrapped wrapped);
            
            @Override
            public Iterable<TElem> query() {
                List<TElem> lst_result= new ArrayList<>();
                for (TWrapped row : virtualDBSource) {
                    boolean isOk= true;
                    for (IPredicate<TWrapped> filter : lst_filter) {
                        if (!filter.apply(row)){
                            isOk= false;
                            continue;
                        }
                    }
                    
                    if (isOk) lst_result.add(wrap(row));
                }
                return lst_result;
            }
            
            public Querier(List<TWrapped> virtualDBSource) {
                this.virtualDBSource= virtualDBSource;
            }

            @Override
            public <T> IWhereThen<IQuerier<TElem>, T> where(IField<T> field) {
                final Field<T, TWrapped> f2= (Field<T, TWrapped>) field;
                
                return new IWhereThen<IQuerier<TElem>, T>() {

                    @Override
                    public IAppendThen<IQuerier<TElem>, T> equalsTo(T value) {
                        // TODO Auto-generated method stub
                        return null;
                    }

                    @Override
                    public IAppendThen<IQuerier<TElem>, T> less(T value) {
                        // TODO Auto-generated method stub
                        return null;
                    }

                    @Override
                    public IAppendThen<IQuerier<TElem>, T> greater(T value) {
                        // TODO Auto-generated method stub
                        return null;
                    }

                    @Override
                    public IAppendThen<IQuerier<TElem>, T> contain(final T value) {
                        lst_filter.add(new IPredicate<TWrapped>() {
                            @Override
                            public boolean apply(TWrapped row) {
                                return f2.getValue(row).toString().contains(value.toString());
                            }
                        });
                        return null;
                    }
                };
            }

            @Override
            public IQuerier<TElem> reset() {
                lst_filter.clear();
                return this;
            }
        }
        
        protected static abstract class Transactor<TElem extends IManager.IElem, TWrapped> implements ITransactor<TElem> {
            private List<TWrapped> virtualDBSource = new ArrayList<>();
            
            public abstract TWrapped unwrap(TElem elem);
            
            public abstract TWrapped towraped(TElem elem);
            
            public abstract void rewrap(TElem elem, TWrapped elem_wrapped);
            
            @Override
            public boolean add(TElem elem) {
                try {
                    TWrapped wraped= towraped(elem);
                    
                    if (!virtualDBSource.add(wraped)) return false;
                    rewrap(elem, wraped);
                } catch (Exception e) {
                    return false;
                }
                
                return true;
            }

            @Override
            public boolean remove(TElem elem) {
                return virtualDBSource.remove(unwrap(elem));
            }

            @Override
            public boolean update(TElem elem) {
                try {
                    int index= virtualDBSource.indexOf(unwrap(elem));
                    
                    TWrapped wraped= towraped(elem);
                    virtualDBSource.set(index, wraped);
                    
                    rewrap(elem, wraped);
                } catch (Exception e) {
                    return false;
                }
                return true;
            }
            
            public Transactor(List<TWrapped> virtualDBSource) {
                this.virtualDBSource= virtualDBSource;
            }
            
        }
        
        protected static class Wrapper<TWrapped> implements IWrapper<TWrapped> {
            private TWrapped value;
            
            @Override
            public TWrapped get() {
                return value;
            }
            
            public Wrapper<TWrapped> reset(TWrapped value) {
                this.value= value;
                return this;
            }
            
            public Wrapper(TWrapped value) {
                reset(value);
            }
        }
    }
    
    private static class StoreManager 
                     extends AbstractManager<IStore, StoreManager.Store>
                     implements IStoreManager{
        private List<Store> virtualDBSource = new ArrayList<>();
        
        private ITransactor<IStore> transactor= new Transactor<IStore, Store>(virtualDBSource){

            @Override
            public Store unwrap(IStore elem) {
                WrapperStore w= (WrapperStore) elem;
                return w.get();
            }

            @Override
            public Store towraped(IStore elem) {
                return new Store(elem, unwrap(elem).getBentos());
            }

            @Override
            public void rewrap(IStore elem, Store elem_wrapped) {
                WrapperStore w= (WrapperStore) elem;
                w.reset(elem_wrapped);
            }
            
        };
        
        private IQuerier<IStore> querier= new Querier<IStore,Store>(virtualDBSource){
            @Override
            protected IStore wrap(Store wrapped) {
                return new WrapperStore(wrapped, transactor);
            }
        };
        
        private IField<String> nameField= new Field<String, Store>("Name") {
            @Override
            public String getValue(Store row) {
                return row.getName();
            }
        };
        private IField<String> telField= new Field<String, Store>("Tel") {
            @Override
            public String getValue(Store row) {
                return row.getTel();
            }
        };

        @Override
        public IStore create() {
            return new WrapperStore(transactor);
        }
        
        @Override
        public String toString() {
            return "{size=" + size() + ", " + super.toString() + "}";
        }
        
        @Override
        public IField<String> getNameField() {
            return nameField;
        }

        @Override
        public IField<String> getTelField() {
            return telField;
        }
        
        public StoreManager() {
            setQuerier(querier);
            setTransactor(transactor);
        }
        
        private static class Store implements bento.types.IStore {
            private String name;
            private String tel;
            private IBentoManager bentos;
            
            @Override
            public String getName() {
                return name;
            }

            @Override
            public bento.types.IStore setName(String name) {
                this.name= name;
                return this;
            }

            @Override
            public String getTel() {
                return tel;
            }

            @Override
            public bento.types.IStore setTel(String tel) {
                this.tel= tel;
                return this;
            }
            
            public IBentoManager getBentos() {
                return bentos;
            }
            
            public Store() {
                bentos= new BentoManager();
            }
            
            public Store(IStore elem, IBentoManager bentos) {
                name= elem.getName();
                tel= elem.getTel();
                this.bentos= bentos;
            }
        }
        
        private static class WrapperStore extends Wrapper<Store> implements IStore {
            private String name;
            private String tel;
            
            private ITransactor<IStore> transactor;
            
            @Override
            public void refresh() {
                name= get().getName();
                tel= get().getTel();
                get().getBentos().refresh();
            }
        
            @Override
            public void commit() {
                transactor.update(this);
            }
        
            @Override
            public String getName() {
                return name;
            }
        
            @Override
            public IStore setName(String name) {
                this.name= name;
                return this;
            }
        
            @Override
            public String getTel() {
                return tel;
            }
        
            @Override
            public IStore setTel(String tel) {
                this.tel= tel;
                return this;
            }
        
            @Override
            public IBentoManager getBentos() {
                return get().getBentos();
            }
            
            @Override
            public String toString() {
                String s= "{name='" + name + "', tel='" + tel + "'";
                s+= ", bentos=" + get().getBentos().toString() + "}";
                
                return s;
            }
            
            public WrapperStore(ITransactor<IStore> transactor) {
                this(new Store(), transactor);
            }
            
            public WrapperStore(Store store, ITransactor<IStore> transactor) {
                super(store);
                this.transactor= transactor;
                refresh();
            }
        }
        
        private static class BentoManager extends AbstractManager<IBentoManager.IBento, BentoManager.Bento> 
                                          implements IBentoManager {
            
            private List<Bento> virtualDBSource = new ArrayList<>();
            
            private ITransactor<IBento> transactor= new Transactor<IBento, Bento>(virtualDBSource){
                @Override
                public Bento unwrap(IBento elem) {
                    WrapperBento w= (WrapperBento) elem;
                    return w.get();
                }

                @Override
                public Bento towraped(IBento elem) {
                    return new Bento(elem);
                }

                @Override
                public void rewrap(IBento elem, Bento elem_wrapped) {
                    WrapperBento w= (WrapperBento) elem;
                    w.reset(elem_wrapped);
                }
            };
            
            private IQuerier<IBento> querier= new Querier<IBento,Bento>(virtualDBSource){
                @Override
                protected IBento wrap(Bento wrapped) {
                    return new WrapperBento(wrapped, transactor);
                }
            };
            
            @Override
            public IBento create() {
                return new WrapperBento(transactor);
            }
            
            @Override
            public String toString() {
                return "{size=" + size() + ", " + super.toString() + "}";
            }
            
            public BentoManager() {
                setQuerier(querier);
                setTransactor(transactor);
            }
            
            public static class Bento implements bento.types.IStore.IBento {
                private String name;
                private int price;
                
                @Override
                public String getName() {
                    return name;
                }

                @Override
                public bento.types.IStore.IBento setName(String name) {
                    this.name= name;
                    return this;
                }

                @Override
                public int getPrice() {
                    return price;
                }

                @Override
                public bento.types.IStore.IBento setPrice(int price) {
                    this.price= price;
                    return this;
                }

                public Bento() {
                    
                }
                
                public Bento(IBento elem) {
                    name= elem.getName();
                    price= elem.getPrice();
                }
            }
            
            private static class WrapperBento extends Wrapper<Bento> implements IBento {
                private String name;
                private int price;
                private ITransactor<IBento> transactor;
                
                @Override
                public void refresh() {
                    name= get().getName();
                    price= get().getPrice();
                }

                @Override
                public void commit() {
                    transactor.update(this);
                }

                @Override
                public String getName() {
                    return name;
                }

                @Override
                public IBento setName(String name) {
                    this.name= name;
                    return this;
                }

                @Override
                public int getPrice() {
                    return price;
                }

                @Override
                public IBento setPrice(int price) {
                    this.price= price;
                    return this;
                }
                
                @Override
                public String toString() {
                    return "{name='" + name + "', price=" + price + "}"; 
                }
                
                public WrapperBento(ITransactor<IBento> transactor) {
                    this(new Bento(), transactor);
                }
                
                public WrapperBento(Bento value, ITransactor<IBento> transactor) {
                    super(value);
                    this.transactor= transactor;
                    refresh();
                }
            }
        }

    }
//    
//    private static abstract class AbstractOrderManager 
//                     extends AbstractManager<IOrderManager.IOrder>
//                     implements IOrderManager{
//    }
//    
//    private static abstract class AbstractAcountManager 
//                     implements IAcountManager {
//        
//        //private ArrayList<AbstractAcountManager.AbstractAcount> _list;
//        private AbstractManager<Acount> _list= new AbstractManager<Acount>() {
//            
//            @Override
//            public void commit() {
//                // TODO Auto-generated method stub
//                
//            }
//            
//            @Override
//            public void refresh() {
//                // TODO Auto-generated method stub
//                
//            }
//        };
//        
//        @Override
//        public int size() {
//            return _list.size();
//        }
//
//        @Override
//        public boolean isEmpty() {
//            return _list.isEmpty();
//        }
//
//        @Override
//        public boolean contains(Object o) {
//            return _list.contains(o);
//        }
//
//        @Override
//        public Iterator<IAcount> iterator() {
//            final Iterator<Acount> iterator = _list.iterator();
//            return new Iterator<IBentoClient.IAcountManager.IAcount>() {
//                
//                @Override
//                public void remove() {
//                    iterator.remove();
//                }
//                
//                @Override
//                public IAcount next() {
//                    return iterator.next();
//                }
//                
//                @Override
//                public boolean hasNext() {
//                    return iterator.hasNext();
//                }
//            };
//        }
//
//        @Override
//        public Object[] toArray() {
//            return _list.toArray();
//        }
//
//        @Override
//        public <T> T[] toArray(T[] a) {
//            return _list.toArray(a);
//        }
//
//        @Override
//        public boolean add(IAcount e) {
//            try{
//                return _list.add(new Acount((IAcountX)e));
//            }catch(Exception ex) {
//                ex.printStackTrace();
//            }
//            return false;
//        }
//
//        @Override
//        public boolean remove(Object o) {
//            return _list.remove(o);
//        }
//
//        @Override
//        public boolean containsAll(Collection<?> c) {
//            return _list.containsAll(c);
//        }
//
//        @Override
//        public boolean addAll(Collection<? extends IAcount> c) {
//            try{
//                for (IAcount a : c) {
//                    this.add(a);
//                }
//            }catch(Exception ex) {
//                ex.printStackTrace();
//            }
//            return false;
//        }
//
//        @Override
//        public boolean addAll(int index, Collection<? extends IAcount> c) {
//            try{
//                List<Acount> lst= new ArrayList<>(c.size());
//                for (IAcount a : c) {
//                    lst.add(new Acount((IAcountX)a));
//                }
//                return _list.addAll(index, lst);
//            }catch(Exception ex) {
//                ex.printStackTrace();
//            }
//            
//            return false;
//        }
//
//        @Override
//        public boolean removeAll(Collection<?> c) {
//            return _list.removeAll(c);
//        }
//
//        @Override
//        public boolean retainAll(Collection<?> c) {
//            return _list.retainAll(c);
//        }
//
//        @Override
//        public void clear() {
//            _list.clear();
//        }
//
//        @Override
//        public IAcount get(int index) {
//            return _list.get(index);
//        }
//
//        @Override
//        public IAcount set(int index, IAcount element) {
//            try{
//                return _list.set(index, new Acount((IAcountX)element));
//            }catch(Exception ex) {
//                ex.printStackTrace();
//            }
//            
//            return null;
//        }
//
//        @Override
//        public void add(int index, IAcount element) {
//            _list.add(index, new Acount((IAcountX)element));
//        }
//
//        @Override
//        public IAcount remove(int index) {
//            return _list.remove(index);
//        }
//
//        @Override
//        public int indexOf(Object o) {
//            return _list.indexOf(o);
//        }
//
//        @Override
//        public int lastIndexOf(Object o) {
//            return _list.lastIndexOf(o);
//        }
//
//        @Override
//        public ListIterator<IAcount> listIterator() {
//            return listIterator(0);
//        }
//
//        @Override
//        public ListIterator<IAcount> listIterator(int index) {
//            final ListIterator<Acount> itor = _list.listIterator(index);
//            
//            return new ListIterator<IBentoClient.IAcountManager.IAcount>() {
//                
//                @Override
//                public void set(IAcount e) {
//                    itor.set(new Acount((IAcountX) e));
//                }
//                
//                @Override
//                public void remove() {
//                    itor.remove();
//                }
//                
//                @Override
//                public int previousIndex() {
//                    return itor.previousIndex();
//                }
//                
//                @Override
//                public IAcount previous() {
//                    return itor.previous();
//                }
//                
//                @Override
//                public int nextIndex() {
//                    return itor.nextIndex();
//                }
//                
//                @Override
//                public IAcount next() {
//                    return itor.next();
//                }
//                
//                @Override
//                public boolean hasPrevious() {
//                    return itor.hasPrevious();
//                }
//                
//                @Override
//                public boolean hasNext() {
//                    return itor.hasNext();
//                }
//                
//                @Override
//                public void add(IAcount e) {
//                    itor.add(new Acount((IAcountX) e));
//                }
//            };
//        }
//
//        @Override
//        public List<IAcount> subList(int fromIndex, int toIndex) {
//            List<Acount> lst_tmp = _list.subList(fromIndex, toIndex);
//            List<IAcount> lst_result= new ArrayList<>(lst_tmp.size());
//            for (Acount a : lst_tmp) {
//                lst_result.add(a);
//            }
//            
//            return lst_result;
//        }
//
//        public List<Acount> cast() {
//            return _list;
//        }
//    }
    
}
